package com.appkubes.fintech.restful.application.face.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.appkubes.common.utils.data.string.UUIDUtils;
import com.appkubes.fintech.common.response.Response;
import com.appkubes.fintech.core.po.FintechUser;
import com.appkubes.fintech.core.po.FintechUserBaseInfo;
import com.appkubes.fintech.core.po.FintechUserPersonalWithBLOBs;
import com.appkubes.fintech.restful.application.face.service.FaceAuthService;
import com.appkubes.fintech.restful.application.face.service.IdCardAuthService;
import com.appkubes.fintech.restful.application.file.dao.IDCardDao;
import com.appkubes.fintech.restful.application.mall.service.MallService;
import com.appkubes.fintech.restful.application.user.dao.UserBaseInfoDao;
import com.appkubes.fintech.restful.application.user.dao.UserDao;
import com.appkubes.fintech.restful.application.user.domain.UserCertificateDomain;
import com.appkubes.fintech.restful.util.HttpUtils;
import com.appkubes.fintech.restful.util.Sha1Uitls;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.faceid.v20180301.FaceidClient;
import com.tencentcloudapi.faceid.v20180301.models.IdCardVerificationRequest;
import com.tencentcloudapi.faceid.v20180301.models.IdCardVerificationResponse;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;

/**
 * 类名: IdCardAuthServiceImpl
 * 描述: TODO
 * 日期: 2020/4/12 23:26
 *
 * @author Reboot65
 * @version 1.0
 **/
@Service
@Slf4j
public class IdCardAuthServiceImpl implements IdCardAuthService {

    public static final int GET_OCR_MAX_RETRY = 3;
    private static String version = "1.0.0";

    private static String OCR_RESULT_API_URL = "https://idasc.webank.com/api/server/getOcrResult";

    @Value("${idcard.faceauth.appId}")
    private String wbappid;

    @Autowired
    private FaceAuthService faceAuthService;

    @Resource
    IDCardDao idCardDao;

    @Value("${idcard.image.base.path}")
    String imagePath;

    @Value("${idcard.faceauth.cloudSecretId}")
    String cloudSecretId;

    @Value("${idcard.faceauth.cloudSecretKey}")
    String cloudSecretKey;

    @Resource
    UserDao userDao;

    @Resource
    UserCertificateDomain certificateDomain;

    @Autowired
    UserBaseInfoDao userBaseInfoDao;

    @Resource
    MallService mallService;

    @Override
    public Response getOcrData(String userKey) {
        FintechUserPersonalWithBLOBs userPersonal = idCardDao.selectByPrimaryKey(userKey);
        if(Objects.isNull(userPersonal)){
            return Response.fail("无数据");
        }
        HashMap<String,Object> data = new HashMap<>();
        if(!StringUtils.isEmpty(userPersonal.getFrontOcr())){
            data.put("front",JSONObject.parseObject(userPersonal.getFrontOcr()));
        }
        if(!StringUtils.isEmpty(userPersonal.getBackOcr())){
            data.put("back",JSONObject.parseObject(userPersonal.getBackOcr()));
        }
        return Response.success(data);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response idCardVerify(String userKey) {
        FintechUser user = getFintechUser(userKey);
        if(null != user.getIsRealNameCertificate() && user.getIsRealNameCertificate() >= 1){
            return Response.success();
        }
        FintechUserPersonalWithBLOBs userPersonal = idCardDao.selectByPrimaryKey(userKey);
        if(Objects.isNull(userPersonal)){
            return Response.fail("请先完成身份证拍摄上传");
        }
        if(StringUtils.isEmpty(userPersonal.getFrontOcr())){
            return Response.fail("请先完成身份证拍摄上传");
        }
        if(StringUtils.isEmpty(userPersonal.getBackOcr())){
            return Response.fail("请先完成身份证拍摄上传");
        }
        JSONObject ocrJson = JSONObject.parseObject(userPersonal.getFrontOcr());
        String name = ocrJson.getString("name");
        String idcard = ocrJson.getString("idcard");
        try {
            IdCardVerificationResponse verificationResponse = identify(name,idcard);
         //   log.info("userKey {} idcard verify-----------result is {}, description is {}",userKey,verificationResponse.getResult(),verificationResponse.getDescription());
            if("0".equals(verificationResponse.getResult())){
                certificateDomain.updatePersonalCertificate(userKey, 1);
                updateUserBaseInfo(userKey,name,idcard);
                return Response.success();
            }else{
                return Response.fail(verificationResponse.getDescription());
            }
        } catch (TencentCloudSDKException e) {
           log.error("身份证校验失败-" +userKey+":" + e.getMessage(),e);
           return Response.fail("身份证校验失败:系统错误");
        }
    }

    private void updateUserBaseInfo(String userKey,String name,String idcard){
        FintechUserBaseInfo userBaseInfo = userBaseInfoDao.selectByPrimaryKey(userKey);
        if (userBaseInfo == null) {
            //补偿基础数据
            userBaseInfo = new FintechUserBaseInfo();
            userBaseInfo.setUserKey(userKey);

            JSONObject data = mallService.getUserHierarchyData(userKey);

            //经纪人信息
            userBaseInfo.setRefUserId(data.getString("parent_user_key"));
            userBaseInfo.setRefUserName(data.getString("parent_user_name"));
            userBaseInfo.setRefUserPhone(data.getString("parent_tel"));

            //推广员信息
            userBaseInfo.setRefChannelId(data.getString("promoter_key"));
            userBaseInfo.setRefChannelName(data.getString("promoter_name"));

            //设备信息
            userBaseInfo.setUserSource(data.getString("utm_source"));
            userBaseInfo.setUserDevice(data.getString("platform_type"));

            userBaseInfo.setUpdateTime(new Date());
            userBaseInfo.setCreateTime(new Date());
            userBaseInfoDao.insertSelective(userBaseInfo);
        }
        userBaseInfo.setRealName(name);
        userBaseInfo.setIdentityNum(idcard);
        userBaseInfoDao.updateByPrimaryKey(userBaseInfo);
    }

    private IdCardVerificationResponse identify(String name,String idCard) throws TencentCloudSDKException{
//        测试使用
//        IdCardVerificationResponse response = new IdCardVerificationResponse();
//        response.setResult("0");
//        return response;
        // 实例化一个认证对象，入参需要传入腾讯云账户secretId，secretKey
        Credential cred = new Credential(cloudSecretId, cloudSecretKey);
        // 实例化要请求产品(以cvm为例)的client对象
        ClientProfile clientProfile = new ClientProfile();
        clientProfile.setSignMethod(ClientProfile.SIGN_TC3_256);
        FaceidClient faceidClient = new FaceidClient(cred,"ap-guangzhou",clientProfile);
        IdCardVerificationRequest request = new IdCardVerificationRequest();
        request.setIdCard(idCard);
        request.setName(name);
        IdCardVerificationResponse response = faceidClient.IdCardVerification(request);
        return response;
    }

    @Override
    public Response getOcrResult(String orderNo,String userKey,String frontOrBack) {
        JSONObject resJson = reqOcrResult(orderNo, userKey, frontOrBack,0);
        if(Objects.isNull(resJson)){
            return Response.fail("身份证识别异常,请重试");
        }
        if(!"0".equals(resJson.getString("code"))){
            return Response.fail(resJson.getString("msg"));
        }
        FintechUserPersonalWithBLOBs userPersonal = idCardDao.selectByPrimaryKey(userKey);
        if(null == userPersonal){
            userPersonal = new FintechUserPersonalWithBLOBs();
            userPersonal.setUserKey(userKey);
            idCardDao.insertSelective(userPersonal);
        }
        String imgPath;
        if("front".equalsIgnoreCase(frontOrBack)){
            String frontCode = resJson.getJSONObject("result").getString("frontCode");
            if(!"0".equals(frontCode)){
                return Response.fail("身份证正面识别失败");
            }
            String frontWarnCode = resJson.getJSONObject("result").getString("frontWarnCode");
            if(!"00000000".equals(frontWarnCode)){
                return Response.fail("身份证照片不合规");
            }
            String frontMultiWarning = resJson.getJSONObject("result").getString("frontMultiWarning");
            if(!"00000000".equals(frontMultiWarning)){
                return Response.fail("身份证照片不合规");
            }
            imgPath = saveBase64ToImage(resJson.getJSONObject("result").getString("frontPhoto"),imagePath);
            userPersonal.setIdcardFrontView(imgPath);
            JSONObject resultJson = trimImgField(resJson);
            userPersonal.setFrontOcr(resultJson.toJSONString());
        }else{
            String backCode = resJson.getJSONObject("result").getString("backCode");
            if(!"0".equals(backCode)){
                return Response.fail("身份证反面识别失败");
            }
            String backWarnCode = resJson.getJSONObject("result").getString("backWarnCode");
            if(!"00000000".equals(backWarnCode)){
                return Response.fail("身份证照片不合规");
            }
            String backMultiWarning = resJson.getJSONObject("result").getString("backMultiWarning");
            if(!"00000000".equals(backMultiWarning)){
                return Response.fail("身份证照片不合规");
            }
            imgPath = saveBase64ToImage(resJson.getJSONObject("result").getString("backPhoto"),imagePath);
            userPersonal.setIdcardBackView(imgPath);
            JSONObject resultJson = trimImgField(resJson);
            userPersonal.setBackOcr(resultJson.toJSONString());
        }
        this.idCardDao.updateByPrimaryKeyWithBLOBs(userPersonal);
        return Response.success(imgPath);
    }

    private JSONObject reqOcrResult(String orderNo, String userKey,String frontOrBack,int retryTimes) {
        String accessToken = faceAuthService.getAccessToken();
        String ticket = faceAuthService.getSignTicket(accessToken);
        String nonce = UUID.randomUUID().toString().replaceAll("-","");
        ArrayList<String> list = new ArrayList<String>();
        list.add(wbappid);
        list.add(orderNo);//orderNo
        list.add(nonce);
        list.add(version);
        String signStr = Sha1Uitls.sign(list, ticket);
        HashMap<String,String> params = new HashMap<>();
        params.put("app_id",wbappid);
        params.put("order_no",orderNo);
        params.put("get_file","1");
        params.put("nonce",nonce);
        params.put("version",version);
        params.put("sign",signStr);
        log.info("begin getOcrResult, orderNo is {}, userKey is {}",orderNo,userKey);
        String result = HttpUtils.sendGet(OCR_RESULT_API_URL,params);
        log.info("end getOcrResult, orderNo is {}, userKey is {}, result is {}",orderNo,userKey,result);
        JSONObject json = JSONObject.parseObject(result);
        if(!"0".equals(json.getString("code"))){
            return json;
        }
        String photo;
        if("front".equalsIgnoreCase(frontOrBack)){
            String frontCode = json.getJSONObject("result").getString("frontCode");
            if(!"0".equals(frontCode)){
                return json;
            }
            photo = json.getJSONObject("result").getString("frontPhoto");

        }else{
            String backCode = json.getJSONObject("result").getString("backCode");
            if(!"0".equals(backCode)){
                return json;
            }
            photo =  json.getJSONObject("result").getString("backPhoto");
        }
        if(StringUtils.isEmpty(photo)){
            //识别正确，但是没有图片，再轮询一次
            if(retryTimes > GET_OCR_MAX_RETRY){
                log.info("get ocr img failed,orderNo is {}, userKey is {}",orderNo,userKey);
                return null;
            }
            retryTimes++;
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
            }
            return reqOcrResult(orderNo,userKey,frontOrBack,retryTimes);
        }
        return json;
    }

    @NotNull
    private JSONObject trimImgField(JSONObject resJson) {
        JSONObject resultJson = resJson.getJSONObject("result");
        resultJson.remove("frontPhoto");
        resultJson.remove("backPhoto");
        resultJson.remove("frontCrop");
        resultJson.remove("backCrop");
        resultJson.remove("headPhoto");
        return resultJson;
    }

    public String saveBase64ToImage(String imgStr, String pathName) {
        if(StringUtils.isEmpty(imgStr)){
            throw new RuntimeException("身份证图片保存失败:图片数据为空");
        }
        FileOutputStream fos = null;
        try {
            byte[] b = Base64Utils.decodeFromString(imgStr);
            for (int i = 0; i < b.length; ++i) {
                if (b[i] < 0) {// 调整异常数据
                    b[i] += 256;
                }
            }
            String exName = "jpg";
            String storageName = "/images/" + DateFormatUtils.format(new Date(), "yyyyMMdd") + "/" + UUIDUtils.getUUID() + "." + exName;
            String resFileName = pathName + storageName;
            File localFile = new File(resFileName);
            if (!localFile.getParentFile().exists()) {
                localFile.getParentFile().mkdirs();
                localFile.getParentFile().setExecutable(true, false);
                localFile.getParentFile().setReadable(true, false);
                localFile.getParentFile().setWritable(true, false);
            }
            fos = new FileOutputStream(localFile);
            fos.write(b);
            localFile.setExecutable(true, false);
            localFile.setReadable(true, false);
            localFile.setWritable(true, false);
//            compressImg(resFileName, exName);
            return storageName;
        } catch (Exception e) {
            throw new RuntimeException("身份证图片保存失败:" + e.getMessage());
        }finally {
             if(null != fos){
                 try {
                     fos.close();
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
             }
        }
    }

    public void compressImg(String path, String exName) {
        if ("jpg,png,gif,jpeg,bmp".indexOf(exName.toLowerCase()) >= 0) {
            try {
                double scale = 0.7D;
                double quality = 0.7D;
                Thumbnails.of(new String[]{path}).scale(scale).outputQuality(quality).toFile(path);
            } catch (Exception var7) {
                var7.printStackTrace();
            }
        }
    }

    @Override
    public Response getOcrParams(String userKey) {
        FintechUser user = userDao.selectByPrimaryKey(userKey);
        if(!Objects.isNull(user) && null != user.getIsRealNameCertificate() && user.getIsRealNameCertificate() >= 1){
            return Response.fail("您已完成身份证上传验证，无需重复申请");
        }
        HashMap<String,Object> params = new HashMap();
        String nonce = UUID.randomUUID().toString().replaceAll("-","");
        String orderNo = UUID.randomUUID().toString().replaceAll("-","");
        ArrayList<String> list = new ArrayList<String>();
        list.add(wbappid);
        list.add(userKey);//userId
        list.add(nonce);
        list.add(version);
        String accessToken = faceAuthService.getAccessToken();
        String ticket = faceAuthService.getNONCETicket(accessToken,userKey);
        String signStr = Sha1Uitls.sign(list, ticket);
        params.put("appid", wbappid);
        params.put("version", version);
        params.put("nonce", nonce);
        params.put("sign", signStr);
        params.put("orderNo", orderNo);
        params.put("userId", userKey);
        return Response.success(params);
    }

    FintechUser getFintechUser(String userKey) {
        FintechUser user = userDao.selectByPrimaryKey(userKey);
        if (user == null) {
            //补偿基础数据缺失
            user = new FintechUser();
            user.setUserKey(userKey);
            userDao.insertSelective(user);
            user = userDao.selectByPrimaryKey(userKey);//需要使用数据库的默认值进行重新初始化
        }
        return user;
    }
}
